package com.bw.zzy.partal2.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import com.bw.zzy.partal2.controller.form.DoctorScheduleSlotVO;
import com.bw.zzy.partal2.db.dao.DoctorWorkPlanDao;
import com.bw.zzy.partal2.db.dao.DoctorWorkPlanScheduleDao;
import com.bw.zzy.partal2.exception.GlobalException;
import com.bw.zzy.partal2.pojo.DoctorWorkPlan;
import com.bw.zzy.partal2.pojo.DoctorWorkPlanSchedule;
import com.bw.zzy.partal2.service.DoctorWorkPlanScheduleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

@Service
public class DoctorWorkPlanScheduleServiceImpl implements DoctorWorkPlanScheduleService {
    private String DOCTOR_SCHEDULE_KEY = "doctor_schedule_";

    private HashMap<String, String> APPOINTMENT_SLOT = new HashMap<>(new HashMap<>() {{
        put("1", "08:00");
        put("2", "08:30");
        put("3", "09:00");
        put("4", "09:30");
        put("5", "10:00");
        put("6", "10:30");
        put("7", "11:00");
        put("8", "11:30");
        put("9", "13:00");
        put("10", "13:30");
        put("11", "14:00");
        put("12", "14:30");
        put("13", "15:00");
        put("14", "15:30");
        put("15", "16:00");
    }});

    @Autowired
    private DoctorWorkPlanScheduleDao doctorWorkPlanScheduleDao;

    @Autowired
    private DoctorWorkPlanDao doctorWorkPlanDao;

    @Autowired
    RedisTemplate redisTemplate;


    @Transactional
    void insertScheduleHandle(ArrayList<DoctorWorkPlanSchedule> list) {
        for (DoctorWorkPlanSchedule entity : list) {

            // 解决 数据库存储的 num 为 null
            if (entity.getNum() == null) {
                entity.setNum(0);
            }
            doctorWorkPlanScheduleDao.insert(entity);

            //TODO 通过redis设置缓存
        }
    }


    @Override
    public void insert(ArrayList<DoctorWorkPlanSchedule> list) {
        insertScheduleHandle(list);

        // 避免患者端挂号出现超售的现象 ，所以需要将添加的出诊计划保存到redies中
        // 到就诊时间 就不能在预约了
        addScheduleCache(list);
    }

    @Override
    public ArrayList selectScheduleByDeptSub(Map param) {
        ArrayList<HashMap> list = doctorWorkPlanScheduleDao.selectScheduleByDeptSub(param);

        // 存储最终所有查询到的 并且已经改好数据格式的医生的变量
        ArrayList<HashMap> result = new ArrayList();
        // 循环遍历 list 时 暂时存储 doctorId 的变量
        int tempDoctorId = 0;
        // 循环时暂时存储某一个 医生 的变量
        HashMap doctor = new HashMap();


        for (HashMap map : list) {
            // 取出查询到的数据库中的值
            int doctorId = MapUtil.getInt(map, "doctorId");
            int slot = MapUtil.getInt(map, "slot");
            // 如果当前遍历的不是同一个 doctor
            if (tempDoctorId != doctorId) {
                tempDoctorId = doctorId;
                doctor = map;
                // 替换 doctor 中的slot 在 slot 中加一个 集合 存储时间段
                doctor.replace("slot", new ArrayList<Integer>() {{
                    add(slot);
                }});
                result.add(doctor);

                // 如果遍历的都是一个 doctor 的信息 直接替换 slot 就可以
                // 给slot 加一个 ArrayList 集合，里面存储 查询到的所有 slot
            } else if (doctorId == tempDoctorId) {
                ArrayList<Integer> slotList = (ArrayList) doctor.get("slot");
                // 将当前循环查询到的 slot 加到doctor.slot 中
                slotList.add(slot);
            }
        }

        for (HashMap map : result) {

            // 取出之前存好的  当前循环到的医生的 slot
            ArrayList<Integer> slot = (ArrayList) map.get("slot");
            // 存储 true false 的新slot
            ArrayList tempSlot = new ArrayList();
            for (int i = 1; i <= 15; i++) {
                tempSlot.add(slot.contains(i));
            }

            // 将 新的 true 和false 的集合 存入
            map.replace("slot", tempSlot);
        }
        return result;
    }


    private void addScheduleCache(ArrayList<DoctorWorkPlanSchedule> list) {
        //如果list中没有元素，就不需要创建缓存   就不需要有下面的逻辑
        // 这个 list 就是 数据库中 doctorWorkPlanSchedule 这个表的java对象
        if (list == null || list.size() == 0) {
            return;
        }

        //一个出诊计划中插入到schedule数据表的时间段记录的外键值相同，取出列表第一个元素获得外键
        // 一组提交数据中的 workPlanId 都是一样的
        // 获取 list 表中的 workPlanId的值
        int workPlanId = list.get(0).getWorkPlanId();

        //根据 workPlanId 查询数据库的信息
        ArrayList<HashMap> newList = doctorWorkPlanScheduleDao.selectScheduleByWorkPlanId(workPlanId);

        for (HashMap one : newList) {
            // 在 查询到的数据库信息中 取出 如下两个值
            int id = MapUtil.getInt(one, "scheduleId");
            int slot = MapUtil.getInt(one, "slot");

            // 设置一个 key 值
            String key = DOCTOR_SCHEDULE_KEY + id;

            //把出诊时间段缓存到Redis
            /*
                保存到 redis 为如下结构：
                    doctor_schedule_100     scheduleId  100
                    doctor_schedule_100     slot  1
             */
            // redis 保存 key 为 key value 为 one 这个map
            redisTemplate.opsForHash().putAll(key, one);
            //出诊日期  例如 2024-04-23

            // 将 查询到的数据库信息 以键值对形式保存到 redis
            redisTemplate.opsForHash().putAll(key, one);

            // 在 list 中获取 date 的值  即数据库中保存的时间值
            String date = MapUtil.getStr(one, "date");

            // 获取到数据库中的 slot 代表的具体时间
            String time = APPOINTMENT_SLOT.get(String.valueOf(slot));

              /*
                设置缓存过期时间    2024-04-23  08:00 表示这条数据会在指定的时间就会被删除
                如果画着再 2024-04-23  08:00 挂号的时候不能再挂  08:00 这个号了，
                因为这个时间已经开始接诊了， 只能再  08:00 之后的时间挂号
            */
            /*
            redisTemplate.expireAt() 是 Spring Data Redis 提供的一个方法，它用于在 Redis 中
             为一个键（key）设置一个具体的过期时间。当这个时间到达时，与该键关联的值（value）将被自动删除。
            DateUtil.parse(date + " " + time)这样的代码，它通常意味着正在尝试将一个字符串
             （该字符串由日期部分date和时间部分time组成）解析成一个Date对象
             */

            // 给 之前保存的每一条信息的 key 值 增加一个到期时间 到时间就会删除信息
            redisTemplate.expireAt(key, DateUtil.parse(date + " " + time));
        }

    }


    @Override
    public HashMap selectScheduleByWorkPlanId(int workPlanId) {

        // 数据库查询
        ArrayList<HashMap> list = doctorWorkPlanScheduleDao.selectScheduleByWorkPlanId(workPlanId);
        HashMap result = new HashMap<>();
        ArrayList temp = new ArrayList();

        for (int i = 0; i < list.size(); i++) {
            // 获取当前循环的第一个
            HashMap map = list.get(i);
            int doctorId = MapUtil.getInt(map, "doctorId");
            int scheduleId = MapUtil.getInt(map, "scheduleId");
            int slot = MapUtil.getInt(map, "slot");
            int maximum = MapUtil.getInt(map, "maximum");
            int num = MapUtil.getInt(map, "num");

            // 如果是第一条信息
            if (i == 0) {
                result.put("doctorId", doctorId);
                result.put("maximum", maximum);
            }
            temp.add(new HashMap<>() {{
                put("scheduleId", scheduleId);
                put("slot", slot);
                put("num", num);
            }});
        }
        result.put("slots", temp);

        return result;
    }

    @Override
    public void updateSchedule(Map param) {
        // 删除或添加 新的时间段
        ArrayList<DoctorWorkPlanSchedule> addList = this.insertOrDeleteScheduleHandle(param);

        //把新添加的时间段 加入到redis 缓存中
        addScheduleCache(addList);
    }



    @Transactional
    ArrayList<DoctorWorkPlanSchedule> insertOrDeleteScheduleHandle(Map param) {
        // 更新诊断人数上限
        doctorWorkPlanDao.updateMaximum(param);

        // 获取修改信息时 不会变的参数
        Integer workPlanId = MapUtil.getInt(param, "workPlanId");
        Integer maximum = MapUtil.getInt(param, "maximum");

        // 获取前端处理过的 slots 参数
        ArrayList<DoctorScheduleSlotVO> slots = (ArrayList<DoctorScheduleSlotVO>) param.get("slots");


        // 存储 要插入新增的时间段
        ArrayList<DoctorWorkPlanSchedule> addList = new ArrayList();
        // 存储 要删除的时间段
        ArrayList<Integer> removeList = new ArrayList();

        slots.forEach(one -> {

            // 如果是新增操作
            if (one.getOperate() .equals("insert") ) {
                // 存储要插入的 表的信息
                DoctorWorkPlanSchedule entity = new DoctorWorkPlanSchedule();
                entity.setWorkPlanId(workPlanId);
                entity.setMaximum(maximum);
                entity.setSlot(one.getSlot());
                // 将信息保存到 addList 中
                addList.add(entity);
                // 删除时间段操作
            } else {
                removeList.add(one.getScheduleId()); //时间段保存到删除列表
            }
        });

        // 如果有要删除的 时间段
        if (removeList.size() > 0) {
            long sum = doctorWorkPlanScheduleDao.selectSumNumByIds(removeList);
            // 如果已经有挂号的人  就不能删除该时间段
            if (sum > 0) {
                throw new GlobalException("修改失败，本次修改与已经挂号记录冲突");
            }
            // 如果该时间段没有挂号的人 就可以删除时间段

            doctorWorkPlanScheduleDao.deleteByIds(removeList);

            // 删除 redis 里的时间缓存
            removeList.forEach(one -> {
                String key = DOCTOR_SCHEDULE_KEY + one;
                redisTemplate.delete(key);
            });
        }
        addList.forEach(one -> {
            //添加时间段记录
            // if 解决 数据库保存为null的问题
            if (one.getNum() == null) {
                one.setNum(0);
            }
            doctorWorkPlanScheduleDao.insert(one);
        });
        return addList;
    }


    @Override
    @Transactional
    public void deleteByWorkPlanId(Integer workPlanId) {
        ArrayList<HashMap> list = doctorWorkPlanScheduleDao.selectScheduleByWorkPlanId(workPlanId);
        // 删除时段记录信息
        doctorWorkPlanScheduleDao.deleteByWorkPlanId(workPlanId);

        // 删除redis 缓存

        list.forEach(one -> {
            int scheduleId = MapUtil.getInt(one, "scheduleId");
            String key = DOCTOR_SCHEDULE_KEY + scheduleId;
            redisTemplate.delete(key);
        });
    }
}
